<?php

namespace libs\qoy;
use \ArrayAccess;
/**
 * 注入接口
 * 
 * @property Response\Response $response 输出
 * @property Config\IConfig  $config   配置
 * @property logger\Ilogger  $log 日志
 * @property Filter\IFilter  $filter 拦截器
 * @property Cache\ICache   $cache 缓存
 * @property Crypt\ICrypt   $crypt 加密
 * @property DB\DBNotORM   $dbNotORM  数据集实例
 */

class DI implements ArrayAccess{
    
    protected  $data=array();
    protected  $hitTimes=array();
     /**
     * @var DI $instance 单例
     */
    private static  $instance;
    /**
     * 
     * @return DI 入口单例
     */
    public static function instance() {
        if(empty(self::$instance)){
            self::$instance=new DI();
        }
        return self::$instance;
    }
    
    /**
     * 
     * @param DIClass $instance 
     */
    public function __construct() {
        $this->set("config", array("type"=>"libs\qoy\Config\File","path"=>"./../config/"));
        $this->set("response",$this->config->get("sys.response",array("type"=>"libs\qoy\Response\Json")));
        $this->set("log", $this->config->get("sys.log",array("type"=>"libs\qoy\Logger\Explorer")));
        $this->set("filter",$this->config->get("sys.filter",array("type"=>"libs\qoy\Filter\None")));
        
    }
    
    protected function initService( $key,$config) {
        $rs = NULL;
        if ($config instanceOf Closure) {
            $rs = $config();
        } elseif (is_string($config) && class_exists($config)) {
            $param = $this->config->get("sys.".$key);
            $rs = new $config($param);
        } elseif(is_array($config) && isset($config["type"]) && !empty($config["type"]) && class_exists(trim($config["type"],"\\"))){
            $class=trim($config["type"],"\\");
            unset($config["type"]);
            $rs=new $class($config);
            
        }else{
            $rs = $config;
        }
        if(is_callable(array($rs, 'onInitialize'))) {
             call_user_func(array($rs, 'onInitialize'));
        }
        return $rs;
    }
      /**
     * 统一setter
     *
     * - 1、设置保存service的构造原型，延时创建
     *
     * @param string $key service注册名称，要求唯一，区分大小写
     * @parms mixed $value service的值，可以是具体的值或实例、类名、匿名函数、数组配置
     */ 
    public function set($key, $value) {
       
        $this->resetHit($key);
        $this->data[$key] = $value;
        return $this;
    }
    
    /**
     * 统一getter
     *
     * - 1、获取指定service的值，并根据其原型分不同情况创建
     * - 2、首次创建时，如果service级的构造函数可调用，则调用
     * - 3、每次获取时，如果非共享且service级的初始化函数可调用，则调用
     *
     * @param string $key service注册名称，要求唯一，区分大小写
     * @param mixed $default service不存在时的默认值
     * @param boolean $isShare 是否获取共享service
     * @return mixed 没有此服务时返回NULL
     */ 
    public function get($key, $default = NULL) {
        if (!isset($this->data[$key])) {
            $this->data[$key] = $default;
        }
        $this->recordHitTimes($key);
        if ($this->isFirstHit($key)) {
            $config=$this->data[$key];
            if($config==NULL){
             $config = $this->config->get("sys.".$key);
            }
            $this->data[$key] = $this->initService($key,$config);
        }
        return $this->data[$key];
    }
    
    
    
    public function __call($name, $arguments) {
        
        if (substr($name, 0, 3) == 'set') {
            $key = lcfirst(substr($name, 3));
            return $this->set($key, isset($arguments[0]) ? $arguments[0] : NULL);
        } else if (substr($name, 0, 3) == 'get') {
            $key = lcfirst(substr($name, 3));
            return $this->get($key, isset($arguments[0]) ? $arguments[0] : NULL);
        } else {
            
        }

       /* throw new Exception_InternalServerError(
            T('Call to undefined method DI::{name}() .', array('name' => $name))
        );*/
    }
    public function __get($name) {
        return $this->get($name);
    }
    public function __set($name, $value) {
         $this->set($name,$value);
    }
    public function offsetExists($offset) {
        return isset(self::$data[$offset]);
    }
    public function offsetGet($offset) {
        return $this->get($offset);
    }
    public function offsetSet($offset, $value) {
         $this->set($offset,$value);
    }
    public function offsetUnset($offset) {
        unset($this->data[$offset]);
    }
    protected function resetHit($key) {
        $this->hitTimes[$key] = 0;
    }

    protected function isFirstHit($key) {
        return $this->hitTimes[$key] == 1;
    }

    protected function recordHitTimes($key) {
        
        if (!isset($this->hitTimes[$key])) {
            $this->hitTimes[$key] = 0;
        }
        $this->hitTimes[$key] ++;
    }
   //public $did=0;
    
   

        
}
